`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file UticaCPUFetchStage.v
	@author Andrew D. Zonenberg
	@brief Fetch stage of the pipeline
 */
module UticaCPUFetchStage(
	clk, hold,
	stall, writeback_cachemiss,
	execute_pc, execute_jumping, execute_jump_final,
	decode_bubble, decode_pc,
	ibus_addr, ibus_rd, ibus_dinok, mispredict
	);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations
	input wire clk;
	input wire hold;
	
	input wire stall;
	input wire writeback_cachemiss;
	input wire[31:0] execute_pc;
	input wire execute_jumping;
	input wire[31:0] execute_jump_final;

	output wire decode_bubble;
	output reg[31:0] decode_pc = 0;

	output wire[31:0] ibus_addr;
	output wire ibus_rd;
	input wire ibus_dinok;
	
	output wire mispredict;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// Fetch stuff

	//Old $pc, which is the address of the instruction we just fetched.
	reg[31:0] fetch_pc_old = 32'hbfc00000;
	
	//We can't use combinatorial forwarding because under some circumstances it might not converge.
	//Fix: speculatively fetch the next instruction and if we mispredicted, flush.
	reg[31:0] fetch_pc_predicted = 32'hbfc00000;
	
	//just another name for the same signal
	assign ibus_addr = fetch_pc_predicted;
	assign ibus_rd = !hold;

	reg[31:0] ibus_addr_buf = 0;
	
	reg fetch_bubble = 1;
	
	//combinatorial forwarding of bubble
	//We bubble out one, but not two, stages during a jump.
	reg decode_bubble_raw = 1;
	assign decode_bubble = decode_bubble_raw | !ibus_dinok | execute_jumping_buf;
	
	reg execute_jumping_buf = 0;
	reg stall_buf = 0;
	reg ibus_dinok_buf = 0;
	
	//Asserted whenever we have a misprediction
	wire mispredict_miss;
	assign mispredict_miss = !ibus_dinok;			//Predict hit but not a hit
	wire mispredict_miss_hit;							//Cache hit right after a miss is bad.
																//We need to bubble out the hit instruction UNLESS
																//both are hits to the same instruction
	wire mispredict_hit_hit;							//Double fetch to the same instruction is bad.
	assign mispredict = mispredict_miss || mispredict_miss_hit || mispredict_hit_hit;
	assign mispredict_miss_hit = ibus_dinok && !ibus_dinok_buf && (fetch_pc_predicted != ibus_addr_buf);
	assign mispredict_hit_hit = ibus_dinok && (fetch_pc_predicted == ibus_addr_buf);
	
	//Handling of cache misses on delay slot instructions
	reg[31:0] delay_jump_addr = 0;
	reg delay_jump_active = 0;
	
	//Simple fetch logic - just pull in an instruction every clock
	//unless we're halted
	always @(posedge clk) begin
		
		if(!hold) begin
			fetch_bubble <= 0;

			ibus_dinok_buf <= ibus_dinok;
			ibus_addr_buf <= ibus_addr;
			
			execute_jumping_buf <= execute_jumping;
			stall_buf <= stall;
			
			////////////////////////////////////
			// Fetch prediction
			
			//Predict PC+4 by default
			fetch_pc_predicted <= ibus_addr + 32'h4;
				
			//Bubbles mean stay put
			if(fetch_bubble)
				fetch_pc_predicted <= ibus_addr;
			
			//If stalling, go back to the last address
			if(stall) begin
				if(stall_buf)
					fetch_pc_predicted <= fetch_pc_predicted;
				else
					fetch_pc_predicted <= execute_pc;
			end
				
			//If first cache miss
			if(!ibus_dinok) begin
				fetch_pc_predicted <= fetch_pc_old;
				fetch_pc_old <= fetch_pc_old;
			end
				
			//If jumping, use that address
			if(execute_jumping && !stall)
				fetch_pc_predicted <= execute_jump_final;
				
			//TODO: Speculative branch prediction?
			
			////////////////////////////////////
			
			//Save whatever address we just fetched as current
			if(ibus_dinok && !stall)
				fetch_pc_old <= fetch_pc_predicted;			
				
			//If stalling, jump back to the same address
			//BUGFIX: this should take precedence over jumps
			if(stall) begin
				fetch_pc_old <= execute_pc;
				
				//but still bubble out decode stage
				decode_pc <= fetch_pc_predicted;
				decode_bubble_raw <= 1;
			end
				
			//If executing a jump, set the new PC but don't bubble out decode (delay slot)
			else if(execute_jumping && !stall) begin
				decode_pc <= fetch_pc_predicted;
				fetch_pc_old <= execute_jump_final;
				decode_bubble_raw <= 0;
			end
			
			//Push bubbles down the pipe			
			else begin
				decode_pc <= fetch_pc_predicted;
				decode_bubble_raw <= fetch_bubble;
			end
			
			////////////////////////////////////
			
			/*
				Cache misses on delay-slot instructions need special handling.
				When we execute the jump, the delay slot instruction is in the decode stage.
				
				If we're jumping and the delay slot was a cache hit, execute normally.
			 */
			
			//If jumping and delay slot was cache miss, save the jump target address and re-fetch the delay inst.
			if(execute_jumping && !ibus_dinok && !stall) begin
				
				//Act like a cache miss
				fetch_pc_predicted <= fetch_pc_old;
				fetch_pc_old <= fetch_pc_old;
				decode_bubble_raw <= 1;
				
				//Then save the failed address so we can resume it later
				delay_jump_addr <= execute_jump_final;
				delay_jump_active <= 1;
				
				//Don't act like we're jumping yet
				execute_jumping_buf <= 0;
			end
			
			if(delay_jump_active) begin
				//If we had a cache miss, it'll be handled just like any other cache miss (in theory).
			
				//If cache hit and there's a saved jump address, jump to it.
				if(ibus_dinok && ibus_addr_buf == fetch_pc_predicted) begin
					
					fetch_pc_predicted <= delay_jump_addr;
					fetch_pc_old <= delay_jump_addr;
					delay_jump_active <= 0;
				end
				
			end
			
		end
	end

endmodule
